<html>  
    <head>
        <title>Magpie playground</title>
        <meta charset="utf-8"/>
        <script src="wasm_exec.js"></script>

        <script>
            const go = new Go();
            WebAssembly.instantiateStreaming(fetch("magpie.wasm"), go.importObject).then((result) => {
                go.run(result.instance);
            });
        </script>

    </head>
    <body>
        <style type="text/css" media="screen">
            #editor { 
                width: 100%;
                height: 350px;
            }
            .magpie-error {
                position:absolute;
                background:rgba(100,200,100,0.5);
                z-index:20
            }
            select option {
                font-size: 16px;
            }
        </style>

<script>

//example comboBox onchange event function
function changeExample(examples, ace_editor) {
    //examples
    let invalidExample = "";
    let basicExample = `s1 = "hello, 黄"  // strings are UTF-8 encoded
println(s1)

三 = 3                 // UTF-8 identifier
println(三)

i = 20_000_000         // int
println(i)

u = 10u                // uint
println(u)

f = 123_456.789_012    // float
println(f)

b = true               // bool
println(b)

a = [1, "2"]           // array
println(a)

h = {"a": 1, "b": 2}   // hash
println(h)

t = (1,2,3)            // tuple
println(t)

dt = dt/2018-01-01 12:01:00/  //datetime literal
println(dt)

n = nil
println(n)
`;

    let constExample = `const PI = 3.14159
//PI = 3.14 //error
println(PI)

const (
    INT,    //default to 0
    DOUBLE,
    STRING = 5,
    ARRAY
)
let i = INT
println(i)   //0
print(ARRAY) //6
`;

    let ifExample = `//if
a, b = 10, 5
if (a > b) {
    println("a > b")
}
elif a == b {
    println("a = b")
}
else {
    println("a < b")
}

if 10.isEven() {
    println("10 is even")
}

if 9.isOdd() {
    println("9 is odd")
}`;

    let forExample = `i = 9
for { // forever loop
    i = i + 2
    if (i > 20) { break }
    println('i = {i}')
}

i = 0
for (i = 0; i < 5; i++) {  // c-like for, '()' is a must
    if (i > 4) { break }
    if (i == 2) { continue }
    println('i is {i}')
}

# for x in arr <where expr> {}
let a = [1,2,3,4]
for i in a where i % 2 != 0 {
    println(i)
}`;

    let whileExample = `i = 10
while (i-- > 3) {
    if i == 7 { continue }
    if i == 5 { break }
    println('i={i}')
}
`;

    let doExample = `i = 10
do {
    i--
    printf("i = %d\\n", i)
    if (i==3) { break }
}
`;

    let caseInExample = `let i = [{"a": 1, "b": 2}, 10]
let x = [{"a": 1, "b": 2}, 10]
case i in {
    1, 2 { println("i matched 1, 2") }
    3    { println("i matched 3") }
    x    { println("i matched x") }
    else { println("i not matched anything")}
}
`;

    let arrayExample = `a = [1,2,3,4]
for i in a where i % 2 != 0 {
    println(i)
}

if ([].empty()) {
    println("array is empty")
}

a.push(5)

revArr = reverse(a)
println("Reversed Array = ", revArr)`;

    let hashExample = `hashObj = {
    12     : "twelve",
    true   : 1,
    "Name" : "HHF"
}
println(hashObj)

hashObj += {"key1" : "value1"}
hashObj -= "key1"
hashObj.push(15, "fifteen") //first parameter is the key, second is the value

hs = {"a": 1, "b": 2, "c": 3, "d": 4, "e": 5, "f": 6, "g": 7}
for k, v in hs where v % 2 == 0 {
    println('{k} : {v}')
}

doc = {
    "one": {
        "two":  { "three": [1, 2, 3], "six":(1,2,3)},
        "four": { "five":  [11, 22, 33]},
    },
}

// same as below
//doc[one][two][three][2] = 44
doc["one"]["two"]["three"][2] = 44
printf("doc[one][two][three][2]=%v\\n", doc["one"]["two"]["three"][2])

doc.one.four.five = 4
printf("doc.one.four.five=%v\\n", doc.one.four.five)
`;

    let tupleExample = `t = () //same as 't = tuple()'

for i in (1,2,3) {
    println(i)
}

//t[0] = 10 //error
`;

    let dtLiteralExample = `let month = "01"
let dt0 = dt/2018-{month}-01 12:01:00/
println(dt0)

let dt1 = dt/2018-01-01 12:01:00/.addDate(1, 2, 3).add(time.SECOND * 10) //add 1 year, two months, three days and 10 seconds
printf("dt1 = %v\\n", dt1)

/* 'datetime literal' + string:
     string support 'YMDhms' where
       Y:Year    M:Month    D:Day
       h:hour    m:minute   s:second

*/
//same result as 'dt1'
let dt2 = dt/2018-01-01 12:01:00/ + "1Y2M3D10s" //add 1 year, two months, three days and 10 seconds
printf("dt2 = %v\\n", dt2)
//same resutl as above
//printf("dt2 = %s\\n", dt2.toStr()) //use 'toStr()' method to convert datetime to string.

let dt3 = dt/2019-01-01 12:01:00/
//you could also use strftiem() to convert time object to string. below code converts time object to 'yyyy/mm/dd hh:mm:ss'
format = dt3.strftime("%Y/%m/%d %T")
println(dt3.toStr(format))

////////////////////////////////
// time object to timestamp
////////////////////////////////
println(dt3.unix()) //to timestamp(UTC)
println(dt3.unixNano()) //to timestamp(UTC)
println(dt3.unixLocal()) //to timestamp(LOCAL)
println(dt3.unixLocalNano()) //to timestamp(LOCAL)

////////////////////////////////
// timestamp to time object
////////////////////////////////
timestampUTC = dt3.unix()      //to timestamp(UTC)
println(unixTime(timestampUTC)) //timestamp to time

timestampLocal = dt3.unixLocal() //to timestamp(LOCAL)
println(unixTime(timestampLocal)) //timestamp to time

////////////////////////////////
// datetime comparation
////////////////////////////////
//two datetime literals could be compared using '>', '>=', '<', '<=' and '=='
let dt4 = dt/2018-01-01 12:01:00/
let dt5 = dt/2019-01-01 12:01:00/

println(dt4 <= dt5) //returns true
`;

    let regexExample = `//Use regular expression literal( /pattern/.match(str) )
let regex = /\\d+\\t/.match("abc 123	mnj")
if (regex) { println("regex matched using regular expression literal") }

//Use 'regexp' module
if regexp.compile(\`\`\\d+\\t\`\`).match("abc 123	mnj") {
    println("regex matched using 'regexp' module")
}

//Use '=~'(str =~ pattern)
if "abc 123	mnj" =~ \`\`\\d+\\t\`\` {
    println("regex matched using '=~'")
}else {
    println("regex not matched using '=~'")
}
`;

    let grepMapExample = `let sourceArr = [2,4,6,8,10,12]

let m = grep  $_ > 5, sourceArr
printf("m is %v\\n", m)

let cp = map $_ * 2 , sourceArr
printf("cp is %v\\n", cp)

//a little bit more complex example
let fields = {
                "animal"   : "dog",
                "building" : "house",
                "colour"   : "red",
                "fruit"    : "apple"
             }
let pattern = \`\`animal|fruit\`\`
// =~(match), !~(unmatch)
let values = map { fields[$_] } grep { $_ =~ pattern } fields.keys()
println(values)
`;

    let conversionExample = `// convert to string using str() function
x = str(10) // convert 10 to string  
printf("x=%s\\n", x)

// convert to int using int() function
x1 = int("10")   // x1 = 10
x2 = +"10" // using +"string" to convert string to int
printf("x1=%d, x2=%d\\n", x1, x2)

y1 = int("0x10") // y1 = 16
y2 = +"0x10" // same as above
printf("y1=%d, y2=%d\\n", y1, y2)

// convert to float using float() funciton
x = float("10.2")
printf("x=%f\\n", x)

// convert to array using array() funciton
x = array("10") // x = ["10"]
y = array((1, 2, 3)) // convert tuple to array
printf("x=%v, y=%v\\n", x, y)

// convert to tuple using tuple() funciton
x = tuple("10") // x = ("10",)
y = tuple([1, 2, 3]) // convert array to tuple
printf("x=%v, y=%v\\n", x, y)

// convert to hash using hash() function
x = hash(["name", "jack", "age", 20]) // array->hash: x = {"name" : "jack", "age" : 20}
y = hash(("name", "jack", "age", 20)) // tuple->hash: x = {"name" : "jack", "age" : 20}
printf("x=%v, y=%v\\n", x, y)

// if the above conversion functions have no arguments, they simply return
// new corresponding types
i = int()   // i = 0
f = float() // f = 0.0
s = str()   // s = ""
h = hash()  // h = {}
a = array() // a = []
t = tuple() // t = ()
printf("i=%v, f=%v, s = %v, h=%v, a=%v, t=%v\\n", i, f, s, h, a, t)
`;

    let funcExample = `//Function with default values and variadic parameters
add = fn(x, y=5, z=7, args...) {
    w = x + y + z
    for i in args {
        w += i
    }
    return w
}
w = add(2,3,4,5,6,7)
println(w)

let z = (x,y) => x * y + 5
println(z(3,4)) //result :17

# multiple returns
fn testReturn(a, b, c, d=40) {
    return a, b, c, d
}
x, y, c, d = testReturn(10, 20, 30) // d is 40
printf("x=[%d], y=[%d], c=[%d], d=[%d]\\n", x, y, c, d)
`;

    let exceptionExample = `// Note: Only support throw string type
let exceptStr = "SUMERROR"
try {
    let th = 1 + 2
    if (th == 3) { throw exceptStr }
}
catch "OTHERERROR" {
    println("Catched OTHERERROR")
}
catch exceptStr {
    println("Catched is SUMERROR")
}
catch {
    println("Catched ALL")
}
finally {
    println("finally running")
}
`;

    let optionalExample = `fn safeDivision?(a, b) {
    if (b == 0){
        return optional.empty();
    } else {
        return optional.of(a/b);
    }
}

op1 = safeDivision?(10, 0)
if !op1.isPresent() {
    println(op1)
}

op2 = safeDivision?(10, 2)
if op2 { // same as 'if op2.isPresent()'
    println(op2)

    let val = op2.get()
    printf("safeDivision?(10, 2)=%d\\n", int(val))
}
`;

    let pipeOperatorExample = `// Test pipe operator(|>)
x1 = ["hello", "world"] |> strings.join(" ") |> strings.upper() |> strings.lower() |> strings.title()
printf("x1=<%s>\\n", x1)

//same as above
x2 = ["hello", "world"] |> strings.join(" ") |> strings.upper |> strings.lower |> strings.title
printf("x2=<%s>\\n", x2)
`;

    let linqExample = `let mm = [1,2,3,4,5,6,7,8,9,10]
result = linq.from(mm).where(x => x % 2 == 0).select(x => x + 2).toSlice()

println('before mm={mm}')
println('after result={result}')

//test embeded linq support
fn TestSimpleLinq() {
    //Prepare Data Source
    let ingredients = [
        {Name: "Sugar",  Calories: 500},
        {Name: "Egg",    Calories: 100},
        {Name: "Milk",   Calories: 150},
        {Name: "Flour",  Calories: 50},
        {Name: "Butter", Calories: 200},
    ]

    //Query Data Source
    ingredient = from i in ingredients where i.Calories >= 150 orderby i.Name select i

    //Display
    for item in ingredient => println(item)
}

fn TestComplexLinq() {
    //Data Source
    stringList = [
        "A penny saved is a penny earned.",
        "The early bird catches the worm.",
        "The pen is mightier than the sword."
    ]

    //Query Data Source
    earlyBirdQuery =
        from sentence in stringList
        let words = sentence.split(" ")
        from word in words
        let w = word.lower()
        where w[0] == "a" || w[0] == "e" ||
              w[0] == "i" || w[0] == "o" ||
              w[0] == "u"
        select word

    //Display
    for v in earlyBirdQuery => printf("'%s' starts with a vowel\\n", v)
}

println()
TestSimpleLinq()

println()
TestComplexLinq()
`;

    let jsonExample = `let hsJson = {"key1" : 10,
              "key2" : "Hello Json %s %s Module",
              "key3" : 15.8912,
              "key4" : [1,2,3.5, "Hello"],
              "key5" : true,
              "key6" : {"subkey1": 12, "subkey2": "Json"},
              "key7" : fn(x,y){x+y}(1,2)
}
let hashStr = json.marshal(hsJson) //same as 'json.toJson(hsJson)'
println(json.indent(hashStr, "  "))
println()

let hsJson1 = json.unmarshal(hashStr)
println(hsJson1)
println()

let arrJson = [1,2.3,"HHF",[],{ "key" : 10, "key1" : 11}]
let arrStr = json.marshal(arrJson)
println(json.indent(arrStr))
println()

let arr1Json = json.unmarshal(arrStr)  //same as 'json.fromJson(arrStr)'
println(arr1Json)
`;

    let userDefinedOperatorExample = `//infix operator '=@' which accept two parameters.
fn =@(x, y) {
    return x + y * y
}
let pp = 10 =@ 5 // Use the '=@' user defined infix operator
printf("pp=%d\\n", pp) // result: pp=35


//prefix operator '=^' which accept only one parameter.
fn =^(x) {
    return -x
}
let hh = =^10 // Use the '=^' prefix operator
printf("hh=%d\\n", hh) // result: hh=-10
`;

    let classNormalExample = `class Animal
{
    let m_name;
    let m_voice;
    
    fn init(name)
    {
        this.m_name = name;
        this.m_voice = "???";
    }
    

    fn getName() { return m_name; }
    fn setName(name) { m_name = name; }
    fn getVoice() { return m_voice; }
    fn setVoice(voice) { m_voice = voice; }

    fn speak() 
    {
        println(m_name + " has voice as " + m_voice);
        println(m_name + " speaks '???! ???! ???!'");
    }

    fn eat()
    {
        println(m_name + " likes eating ???");
    }
}


class Cat : Animal {
    fn init(name)
    {
        parent.init(name)
        m_voice = "Meo";
    }

    fn speak()  
    {
        println(m_name + " has voice as " + m_voice);
        println(m_name + " speaks 'Meo! Meo! Meo!'");
    }

    fn eat()
    {
        println(m_name + " likes eating Mouse");
    }
}

class Dog : Animal {

    fn init(name)
    {
        parent.init(name)
        m_voice = "Gau";
    }

    fn speak()  
    {
        println(m_name + " has voice as " + m_voice);
        println(m_name + " speaks 'Gau! Gau! Gau!'");
    }

    fn eat()
    {
        println(m_name + " likes eating Cat");
    }
}

myAnimal = new Animal("My Animal");
myAnimal.speak();

myDog = new Dog("My Dog");
myDog.speak();

myCat = new Cat("My Cat");
myCat.speak();

myAnimalDog = new Dog("My Annimal Dog");
myAnimalDog.speak();

myAnimalCat = new Cat("My Animal Cat");
myCat.eat();
myDog.eat();

myAnimalDog.eat();
myAnimalCat.eat();

println(myAnimalCat.toString())        //string() method is coming from the root class 'object'
println(myAnimalCat.hashCode())        //hashCode() method is coming from the root class 'object'
println(myAnimalCat.is_a(Cat))         //is_a() method is coming from the root class 'object'
println(myAnimalCat.instanceOf(Cat))   //instanceOf() method is is equal to is_a() method
println(myAnimalCat.classOf())         //classOf() method is coming from the root class 'object'
`;

    let classExtensionExample = `class Animal {
	fn Walk() {
		println("Animal Walk!")
	}
}

//extension methods like objective-c
class Animal (Run) {
	fn Run() {
		println("Animal Run!")
		this.Walk() //call Walk() method of Animal class.
	}
}

animal = new Animal()
animal.Walk()

println()
animal.Run()
`;

    let classAnnotationExample = `class @Test {
  property Enabled
}

//marker annotation
class @Demo {}


class TestExample {
  @Demo
  @Test{Enabled = true}
  fn TestA() {
    printf("TestA is called\\n")
  }

  @Demo
  @Test{Enabled = false}
  fn TestB() {
    printf("TestB is called\\n")
  }

  @Demo
  @Test{Enabled = false}
  fn TestC() {
    printf("TestC is called\\n")
  }

  @Demo
  @Test{Enabled = true}
  fn TestD() {
    printf("TestD is called\\n")
  }
}

testObj = new TestExample()
for method in testObj.getMethods() {
  printf("\\nmethodName=%s\\n", method.name)
  annos = method.getAnnotations()
  for anno in annos {
    //println()
    //printf("ANNO NAME=%s, enabled=%t\\n", anno, anno.Enabled)

    if anno.instanceOf(Test) {
      printf("ANNO NAME=%s, enabled=%t\\n", anno, anno.Enabled)
      if anno.Enabled {
        method.invoke()
      }
    } elif anno.instanceOf(Demo) {
      printf("ANNO NAME=%s \\n", anno)
    }
  }
}
`;

    let classIndexerExample = `class IndexedNames
{
    let namelist = []
    let size = 10
    fn init()
    {
        let i = 0
        for (i = 0; i < size; i++)
        {
            namelist[i] = "N. A."
        }
    }

    fn getNameList() {
        println(namelist)
    }

    property this[index]
    {
        get
        {
            let tmp;
            if ( index >= 0 && index <= size - 1 )
            {
               tmp = namelist[index]
            }
            else
            {
               tmp = ""
            }
     
            return tmp
         }
         set
         {
             if ( index >= 0 && index <= size-1 )
             {
                 namelist[index] = value
             }
         }
    }
}

fn Main()
{
    namesObj = new IndexedNames()

    //Calling Indexer's set function
    namesObj[0] = "Zara"
    namesObj[1] = "Riz"
    namesObj[2] = "Nuha"
    namesObj[3] = "Asif"
    namesObj[4] = "Davinder"
    namesObj[5] = "Sunil"
    namesObj[6] = "Rubic"

    namesObj.getNameList()

    for (i = 0; i < namesObj.size; i++)
    {
        println(namesObj[i]) //Calling Indexer's get function
    }
}

Main()
`;

    let classOperatorOverloadingExample = `class Vector {
	// instance variables
	let x = 0;
	let y = 0;
	let z = 0;

	// constructor
	fn init (a, b, c) {
		if (!a) { a = 0;}
		if (!b) {b = 0;}
		if (!c) {c = 0;}
		x = a; y = b; z = c;
	}

	// instance method (built-in operator overriding)
	fn +(v) {
		if (type(v) == "INTEGER") {
			return new Vector(x+v, y+v, z+v);
		}
		elif v.is_a(Vector) {
			return new Vector(x+v.x, y+v.y, z+v.z);
		}
		return nil;
	}

	fn -() {
		return new Vector(-(x),-(y),-(z))
	}

	fn ++() {
		return new Vector(x+1,y+1,z+1)
	}

	fn --() {
		return new Vector(x-1,y-1,z-1)
	}

	// instance method (built-in String conversion overriding)
	fn String() {
			// string interpolation support
		return fmt.sprintf("(%v),(%v),(%v)", this.x, this.y, this.z);
	}

	fn otherFunc(v) {
		return this + v  //calling the '+' function
	}
}

fn Vectormain() {
	// initialize a new vector object
	v1 = new Vector(1,2,3);
	// initialize a new vector object
	v2 = new Vector(4,5,6);

	// call + function in the vector object
	v3 = v1 + v2 //same as 'v3 = v1.+(v2)'
	// returns string "(5),(7),(9)"
	println(v3.String());

	v4 =v1 + 10 //same as v4 = v1.+(10);
	//returns string "(11),(12),(13)"
	println(v4.String());
	println(v1.otherFunc(8).String())

	v5 = -v4
	println(v5.String())

	v6 = --v4
	println(v6.String())

	v7 = ++v4
	println(v7.String())
}

Vectormain()


class Box {
	let length;
	let breadth;
	let height;

	fn getVolume() {
		return length * breadth * height;
	}

	fn setLength(len) {
		length = len;
	}

	fn setBreadth(bre) {
		breadth = bre;
	}
 
	fn setHeight(hei) {
		height = hei;
	}
	
	// Overload + operator to add two Box objects.
	fn +(other) {
		box = new Box();
		box.length = this.length + other.length;
		box.breadth = this.breadth + other.breadth;
		box.height = this.height + other.height;
		return box;
	}
}

fn Boxmain() {
	// box 1 specification
	Box1 = new Box();
	Box1.setLength(6.0);
	Box1.setBreadth(7.0);
	Box1.setHeight(5.0);

	// box 2 specification
	Box2 = new Box();
	Box2.setLength(12.0);
	Box2.setBreadth(13.0);
	Box2.setHeight(10.0);

	// volume of box 1
	volume = Box1.getVolume();
	printf("Volume of Box1 : %v\\n", volume);

	// volume of box 2
	volume = Box2.getVolume();
	printf("Volume of Box2 : %v\\n", volume);

	// Add two object as follows:
	Box3 = Box1 + Box2;

	// volume of box 3
	volume = Box3.getVolume();
	printf("Volume of Box3 : %v\\n", volume);
}

Boxmain()
`;

    let classPropertyExample = `class Date {
    let month = 7;  // Backing store
    property Month
    {
        get { return month }
        set {
            if ((value > 0) && (value < 13))
            {
                month = value
            } else {
               println("BAD, month is invalid")
            }
        }
    }

    property Year;
    //property Year { get; set;}

    property Day { get; }

    property OtherInfo1 { get; }
    property OtherInfo2 { set; }

    fn init(year, month, day) {
        this.Year = year
        this.Month = month
        this.Day = day
    }

    fn getDateInfo() {
        printf("Year:%v, Month:%v, Day:%v\\n", this.Year, this.Month, this.Day) //note here, you need to use 'this.Property', not 'Property'
    }


}

dateObj = new Date(2000, 5, 11)
//printf("Calling Date's getter, month=%d\\n", dateObj.Month)
dateObj.getDateInfo()

println()
dateObj.Month = 10
printf("dateObj.Month=%d\\n", dateObj.Month)

dateObj.Year = 2018
println()
dateObj.getDateInfo()
`;

    let classStaticExample = `class Test
{
   static let x = 0;
   static let y = 5;

   static fn Main()
   {
      println(Test.x);
      println(Test.y);

      Test.x = 99;
      println(Test.x);
   }
}

Test.Main()
`;

    let exampleArr = [basicExample, constExample,ifExample, forExample,
                      whileExample, doExample, caseInExample, arrayExample,
                      hashExample, tupleExample, dtLiteralExample, regexExample, grepMapExample,
                      conversionExample, funcExample, exceptionExample, 
                      optionalExample,pipeOperatorExample, linqExample, jsonExample,
                      userDefinedOperatorExample,classNormalExample, classExtensionExample,
                      classAnnotationExample, classIndexerExample,classOperatorOverloadingExample,
                      classPropertyExample,classStaticExample];

    var idx = examples.selectedIndex;
    editor = ace.edit("editor");

    //Clears all the annotations
    editor.session.clearAnnotations()

    //remove all markers previously added
    let all_markers = editor.session.getMarkers();
    for(var key in all_markers) {
        if (all_markers[key].type == "fullLine") {
            editor.session.removeMarker(all_markers[key].id);
        }
    }

    editor.setValue(exampleArr[idx]);
    editor.gotoLine(0, 0, false);
}

//theme comboBox onchange event function
function changeTheme(themes, ace_editor) {
    let theme = themes.options[themes.selectedIndex].text
    editor = ace.edit("editor");
    editor.setTheme("ace/theme/" + theme);
}
</script>

Examples:
<select style="min-height:30px;min-width:100px;" name="examples" id="examples" onChange="changeExample(this, 'editor')">
  <option>basic</option>
  <option>constant</option>
  <option>if expression</option>
  <option>for expression</option>
  <option>while expression</option>
  <option>do expression</option>
  <option>case-in expression</option>
  <option>array</option>
  <option>hash</option>
  <option>tuple</option>
  <option>datetime literal</option>
  <option>regular expression</option>
  <option>grep and map</option>
  <option>conversion</option>
  <option>function</option>
  <option>exception handling</option>
  <option>optional</option>
  <option>pipe operator</option>
  <option>linq example</option>
  <option>json example</option>
  <option>user defined operators</option>
  <option>class(normal)</option>
  <option>class(extension)</option>
  <option>class(annotation)</option>
  <option>class(indexer)</option>
  <option>class(operator overloading)</option>
  <option>class(property)</option>
  <option>class(static)</option>
</select>

&nbsp;&nbsp;&nbsp;&nbsp;Editor Themes:
<select style="min-height:30px;min-width:100px;" name="themes" id="themes" onChange="changeTheme(this, 'editor')">
  <option>ambiance</option>
  <option>chaos</option>
  <option>chrome</option>
  <option>clouds</option>
  <option>clouds_midnight</option>
  <option>cobalt</option>
  <option>crimson_editor</option>
  <option>dawn</option>
  <option>dracula</option>
  <option>dreamweaver</option>
  <option>eclipse</option>
  <option>github</option>
  <option>gob</option>
  <option>gruvbox</option>
  <option>idle_fingers</option>
  <option>iplastic</option>
  <option>katzenmilch</option>
  <option>kr_theme</option>
  <option>kuroir</option>
  <option>merbivore</option>
  <option>merbivore_soft</option>
  <option>mono_industrial</option>
  <option>monokai</option>
  <option>nord_dark</option>
  <option>pastel_on_dark</option>
  <option>solarized_dark</option>
  <option selected>solarized_light</option>
  <option>sqlserver</option>
  <option>terminal</option>
  <option>textmate</option>
  <option>tomorrow</option>
  <option>tomorrow_night</option>
  <option>tomorrow_night_blue</option>
  <option>tomorrow_night_bright</option>
  <option>tomorrow_night_eighties</option>
  <option>twilight</option>
  <option>vibrant_ink</option>
  <option>xcode</option>
</select>

<hr style="display:block">
<h3>Input</h3>
<div id="editor"></div>
        <script src="//cdnjs.cloudflare.com/ajax/libs/ace/1.4.5/ace.js" type="text/javascript" charset="utf-8"></script>

        <script>
            //trigger 'examples' onchange event, so the editor will show the first example's content
            var examp = document.getElementById("examples");
            examp.onchange();

            var editor = ace.edit("editor");
            editor.setTheme("ace/theme/solarized_light");
            editor.session.setMode("ace/mode/javascript");
            editor.getSession().setUseWorker(false);
            editor.setOptions({fontSize: "13pt"});
            editor.commands.addCommand({
                name: "run",
                bindKey: {win: "Ctrl-Enter", mac: "Command-Enter"},
                exec: function(editor) { interpret() }
            });
        </script>
        
        <p><button style="background-color:#F4F6F6; color:#00AED0" onclick="interpret();" id="interpretButton">Run code with Ctrl+Enter, or click HERE</button></p>
        <h3 id="output">Output</h3>
        <div style="width: 100%;">
            <textarea style="width: 100%;" id="result" name="result" rows="20"></textarea>
        </div>

    </body>
    <script>
        let interpret = function() {
            let editor = ace.edit("editor");

            //Clears all the annotations
            editor.session.clearAnnotations()

            //remove all markers previously added
            let all_markers = editor.session.getMarkers();
            for(var key in all_markers) {
                if (all_markers[key].type == "fullLine") {
                    editor.session.removeMarker(all_markers[key].id);
                }
            }

            //evaluate code
            let code = editor.getValue();
            let { errlines, output } = magpie_run_code(code)

            let parser_err_annotations = [];
            //check for parser error, and mark line if has error
            var Range = ace.require('ace/range').Range;
            if (errlines != "-1") { //parser error
                let errlinesArr = errlines.split("|")
                for (let i = 0; i < errlinesArr.length; i++) {
                    let errline = errlinesArr[i]
                    editor.session.addMarker(new Range(errline - 1, 0, errline - 1, 2000), "magpie-error", "fullLine");
                    parser_err_annotations.push({ row: errline - 1, text: output, type: "error"})
                }

                // show the error icon
                editor.session.setAnnotations(parser_err_annotations)
            }

            //check for Runtime error, and mark line if has error
            let reg = /Runtime Error:[^]*? at line (\d+)/gm;
            var match;
            let runtime_err_annotations = [];
            var matched = false;
            while (match = reg.exec(output)) {
                matched = true;
                let line = parseInt(match[1]);
                editor.session.addMarker(new Range(line - 1, 0, line - 1, 2000), "magpie-error", "fullLine");
                runtime_err_annotations.push({ row: line - 1, text: match[0], type: "error"});
            }
            if (matched) {
                //setAnnotations takes array of annotations, which replaces annotations from previous call, 
                //so you need to collect all annotations into array and call setAnnotations after that, once
                editor.session.setAnnotations(runtime_err_annotations);
            }

            //output result
            result.value = output

            editor.focus()
        }
     </script>
</html> 

